package com.ymt.bpm.service.platform.statistics.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ymt.bpm.dao.MDao;
import com.ymt.bpm.dao.Mapper;
import com.ymt.bpm.engine.EngineConst;
import com.ymt.bpm.service.engine.bpd.BpdService;
import com.ymt.bpm.service.engine.bpd.BpdVersionCacheService;
import com.ymt.bpm.service.platform.statistics.StatisticsService;
import com.ymt.bpm.util.AES;
import com.ymt.bpm.util.Const;
import com.ymt.bpm.util.DateUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Johnny on 2017/6/27.
 */
@Service
public class StatisticsServiceImpl implements StatisticsService {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    Environment env;

    @Autowired
    private MDao dao;

    @Autowired
    private BpdService bds;

    @Autowired
    private BpdVersionCacheService bvcs;

    @Override
    public String bpdkpi(String tenantId, String param) {

        JSONObject kpi = new JSONObject();
        JSONObject params = JSONObject.parseObject(param);

        //tenantId
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            params.put(Const.TENANT_ID, tenantId);
        }

        //bpdId translate
        String bpdId = AES.decrypt(params.getString("bpdId"));
        if (log.isDebugEnabled()) {
            log.debug("decrypted bpdId="+bpdId);
        }
        params.put("bpdId", bpdId);

        //now time
        Date now = DateUtil.getGMTDate();
        params.put("now", now);

        //date for 30 days ago
        long agotime = now.getTime() - 30 * 86400 * 1000;
        Date ago30 = new Date(agotime);
        params.put("ago30", ago30);

        //re-calculate dates
        int timezoneOffset = params.getInteger(Const.TIMEZONEOFFSET);
        Date curDate1 = DateUtil.getLocaleDate(now, timezoneOffset);
        Date curDate2 = DateUtil.getLocaleDate(now, timezoneOffset);
        Date from = DateUtil.getDateByStr(params.getString("fromStr"));
        Date to = DateUtil.getDateByStr(params.getString("toStr"));
        params.put("from", from);
        params.put("to", to);

        //over due ratio
        double dueWarnRatio = 0.8;
        String dueWarnRatioStr = env.getProperty("due-warn-ratio");
        if (dueWarnRatioStr!=null && StringUtils.isNumeric(dueWarnRatioStr)) {
            dueWarnRatio = Double.parseDouble(dueWarnRatioStr);
        }
        params.put("dueWarnRatio", dueWarnRatio);

        /*String bpdId = params.getString("bpdId");
        String fromStr = params.getString("from");
        String toStr = params.getString("to");
        String orgIdStart = params.getString("orgIdStart");
        String orgIdHandle = params.getString("orgIdHandle");
        String userIdStart = params.getString("userIdStart");
        String userIdHandle = params.getString("userIdHandle");*/

        //deploy number
        int deployNo = getDeployHistory(params);
        kpi.put("no_deploy", deployNo);

        //expected pi due time
        kpi.putAll(getExpectedDuetime(params));
        //pi numbers
        kpi.putAll(getNumbersOfPi(params));
        //ti numbers
        kpi.putAll(getNumbersOfTi(params));
        //duration pi
        kpi.putAll(getDurationsOfPi(params));
        //duration ti
        kpi.putAll(getDurationsOfTi(params));
        //duration ti
        kpi.putAll(getOverduesOfTi(params));
        //pi trend
        kpi.putAll(getTrendOf30Pi(params, timezoneOffset, curDate1));
        //ti trend
        kpi.putAll(getTrendOf30Ti(params, timezoneOffset, curDate2));
        //top 5 users
        if (!params.containsKey("toppi")) params.put("toppi", 5);
        kpi.putAll(getTopUsersOfPi(params));

        if (log.isDebugEnabled()) {
            log.debug("bpdkpi returns:"+kpi);
        }

        return kpi.toString();
    }

    /**
     * 获取版本数量
     * @param params
     * @return
     */
    private int getDeployHistory(JSONObject params) {
        int dh = dao.selectOne(Mapper.StatisticsMapper, "countDeployHistory", params);
        return dh;
    }

    /**
     * 获取流程期望完成时间
     * @param params
     * @return
     */
    private JSONObject getExpectedDuetime(JSONObject params) {
        JSONObject numbers = new JSONObject();
        int versionid = bds.getCurrentVersionId(params.getString(Const.TENANT_ID), params.getString("bpdId"));
        JSONObject version = bvcs.getVersionJson(params.getString(Const.TENANT_ID), params.getString("bpdId"), versionid);
        /*JSONObject otherConfig = version.getJSONObject("otherConfig");
        // expected pi finish time
        int timelineno = otherConfig.getIntValue("timelineno");
        String timelineunit = otherConfig.getString("timelineunit");
        if (timelineunit!=null && timelineunit.length()>0 && timelineno>0) {
            switch (timelineunit) {
                case "day":
                    timelineno = timelineno * 86400;
                    break;
                case "hour":
                    timelineno = timelineno * 3600;
                    break;
                case "minute":
                    timelineno = timelineno * 60;
                    break;
            }
        }
        numbers.put("finishtime_pi_expected", timelineno);

        //expected ti finish times TODO*/
        numbers.put(EngineConst.BPMNCONF, version.getJSONObject(EngineConst.BPMNCONF));
        numbers.put(EngineConst.OTHERCONF, version.getJSONObject(EngineConst.OTHERCONF));

        return numbers;
    }

    /**
     * 获取流程实例的各个运行状态数量，以及各个过期状态数量
     * @param params
     * @return
     */
    private JSONObject getNumbersOfPi(JSONObject params) {
        JSONObject numbers = new JSONObject();

        List<Map<String, Object>> countPiMaps = dao.selectList(Mapper.StatisticsMapper, "countPi", params);
        List<Map<String, Object>> countPiHiMaps = dao.selectList(Mapper.StatisticsMapper, "countPiHi", params);
        List<Map<String, Object>> countPiHiAllMaps = dao.selectList(Mapper.StatisticsMapper, "countPiHiAll", params);
        List<Map<String, Object>> countPiOverdueStatusMaps = dao.selectList(Mapper.StatisticsMapper, "countPiDueStatus", params);
        List<Map<String, Object>> countPiHiOverdueStatusMaps = dao.selectList(Mapper.StatisticsMapper, "countPiHiDueStatus", params);
        List<Map<String, Object>> countPiHiAllOverdueStatusMaps = dao.selectList(Mapper.StatisticsMapper, "countPiHiAllDueStatus", params);

        JSONObject piNumbers = new JSONObject();
        //在途
        if (countPiMaps!=null && countPiMaps.size()>0) {
            for (int i=0; i<countPiMaps.size(); i++) {
                piNumbers.put((String)countPiMaps.get(i).get("STATUS"), countPiMaps.get(i).get("COUNT"));
            }
        }
        //完成（在指定时间内发起的）
        if (countPiHiMaps!=null && countPiHiMaps.size()>0) {
            for (int i=0; i<countPiHiMaps.size(); i++) {
                piNumbers.put((String)countPiHiMaps.get(i).get("STATUS"), countPiHiMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("piNumbers", piNumbers);

        JSONObject pihiAllNumbers = new JSONObject();
        //完成（包括不在指定时间内发起的，以前发起的）
        if (countPiHiAllMaps!=null && countPiHiAllMaps.size()>0) {
            for (int i=0; i<countPiHiAllMaps.size(); i++) {
                pihiAllNumbers.put((String)countPiHiAllMaps.get(i).get("STATUS"), countPiHiAllMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("pihiAllNumbers", pihiAllNumbers);

        //count by overdue state

        //在途
        JSONObject piOverdueStatusNumbers = new JSONObject();
        piOverdueStatusNumbers.put("N",0);
        piOverdueStatusNumbers.put("W",0);
        piOverdueStatusNumbers.put("O",0);
        if (countPiOverdueStatusMaps!=null && countPiOverdueStatusMaps.size()>0) {
            for (int i=0; i<countPiOverdueStatusMaps.size(); i++) {
                piOverdueStatusNumbers.put((String)countPiOverdueStatusMaps.get(i).get("OVERDUE_STATUS"), countPiOverdueStatusMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("piOverdueStatusNumbers", piOverdueStatusNumbers);

        //完成
        JSONObject pihiOverdueStatusNumbers = new JSONObject();
        pihiOverdueStatusNumbers.put("N",0);
        pihiOverdueStatusNumbers.put("W",0);
        pihiOverdueStatusNumbers.put("O",0);
        if (countPiHiOverdueStatusMaps!=null && countPiHiOverdueStatusMaps.size()>0) {
            for (int i=0; i<countPiHiOverdueStatusMaps.size(); i++) {
                pihiOverdueStatusNumbers.put((String)countPiHiOverdueStatusMaps.get(i).get("OVERDUE_STATUS"), countPiHiOverdueStatusMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("pihiOverdueStatusNumbers", pihiOverdueStatusNumbers);

        //所有在此期间完成，包括之前发起
        JSONObject pihiAllOverdueStatusNumbers = new JSONObject();
        pihiAllOverdueStatusNumbers.put("N",0);
        pihiAllOverdueStatusNumbers.put("W",0);
        pihiAllOverdueStatusNumbers.put("O",0);
        if (countPiHiAllOverdueStatusMaps!=null && countPiHiAllOverdueStatusMaps.size()>0) {
            for (int i=0; i<countPiHiOverdueStatusMaps.size(); i++) {
                pihiAllOverdueStatusNumbers.put((String)countPiHiAllOverdueStatusMaps.get(i).get("OVERDUE_STATUS"), countPiHiAllOverdueStatusMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("pihiAllOverdueStatusNumbers", pihiAllOverdueStatusNumbers);

        return numbers;
    }

    /**
     * 根据任务的过期状态统计数量
     * @param params
     * @return
     */
    private JSONObject getNumbersOfTi(JSONObject params) {
        JSONObject numbers = new JSONObject();

        List<Map<String, Object>> countTodoOverdueStatusMaps = dao.selectList(Mapper.StatisticsMapper, "countTodoDueStatus", params);
        List<Map<String, Object>> countDoneOverdueStatusMaps = dao.selectList(Mapper.StatisticsMapper, "countDoneDueStatus", params);

        //此时间段创建的（包括在此时间段之前发起流程的）
        JSONObject todoOverdueStatusNumbers = new JSONObject();
        todoOverdueStatusNumbers.put("N",0);
        todoOverdueStatusNumbers.put("W",0);
        todoOverdueStatusNumbers.put("O",0);
        if (countTodoOverdueStatusMaps!=null && countTodoOverdueStatusMaps.size()>0) {
            for (int i=0; i<countTodoOverdueStatusMaps.size(); i++) {
                todoOverdueStatusNumbers.put((String)countTodoOverdueStatusMaps.get(i).get("OVERDUE_STATUS"), countTodoOverdueStatusMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("todoOverdueStatusNumbers", todoOverdueStatusNumbers);

        //此时间段完成的（包括在此时间段之前发起流程的）
        JSONObject doneOverdueStatusNumbers = new JSONObject();
        doneOverdueStatusNumbers.put("N",0);
        doneOverdueStatusNumbers.put("W",0);
        doneOverdueStatusNumbers.put("O",0);
        if (countDoneOverdueStatusMaps!=null && countDoneOverdueStatusMaps.size()>0) {
            for (int i=0; i<countDoneOverdueStatusMaps.size(); i++) {
                doneOverdueStatusNumbers.put((String)countDoneOverdueStatusMaps.get(i).get("OVERDUE_STATUS"), countDoneOverdueStatusMaps.get(i).get("COUNT"));
            }
        }
        numbers.put("doneOverdueStatusNumbers", doneOverdueStatusNumbers);

        return numbers;
    }

    /**
     * 时间段内完成的流程完成时间
     * @param params
     * @return
     */
    private JSONObject getDurationsOfPi(JSONObject params) {
        JSONObject numbers = new JSONObject();
        Map<String, Object> map = dao.selectOne(Mapper.StatisticsMapper, "getDurationsOfPi", params);
        numbers.put("finishtime_pi_shortest", map.get("MIN"));
        numbers.put("finishtime_pi_longest", map.get("MAX"));
        numbers.put("finishtime_pi_avg", map.get("AVG"));
        return numbers;
    }

    /**
     * 时间段内完成的任务完成时间，包括之前发起的
     * @param params
     * @return
     */
    private JSONObject getDurationsOfTi(JSONObject params) {
        JSONObject numbers = new JSONObject();
        List<Map<String, Object>> maps = dao.selectList(Mapper.StatisticsMapper, "getDurationsOfTi", params);
        JSONObject activityDurations = new JSONObject();
        if (maps!=null && maps.size()>0) {
            for (Map<String, Object> map : maps) {
                JSONObject amap = new JSONObject();
                amap.put("min", map.get("MIN"));
                amap.put("max", map.get("MAX"));
                amap.put("avg", map.get("AVG"));
                amap.put("activityId", map.get("ACTIVITY_ID"));
                activityDurations.put((String)map.get("ACTIVITY_ID"), amap);
            }
        }
        numbers.put("activityDurations", activityDurations);
        return numbers;
    }

    /**
     * 时间段内完成任务的过期情况，包括之前发起的
     * @param params
     * @return
     */
    private JSONObject getOverduesOfTi(JSONObject params) {
        JSONObject numbers = new JSONObject();
        List<Map<String, Object>> maps = dao.selectList(Mapper.StatisticsMapper, "getOverduesOfTi", params);
        JSONObject activityOverdues = new JSONObject();
        if (maps!=null && maps.size()>0) {
            for (Map<String, Object> map : maps) {
                JSONObject amap = new JSONObject();
                amap.put("overdue", map.get("COUNT"));
                amap.put("activityId", map.get("ACTIVITY_ID"));
                activityOverdues.put((String)map.get("ACTIVITY_ID"), amap);
            }
        }
        numbers.put("activityOverdues", activityOverdues);
        return numbers;
    }

    /**
     * 30天内发起、完成、过期的流程数量
     * @param params
     * @param timezoneOffset
     * @param curDate
     * @return
     */
    public JSONObject getTrendOf30Pi(JSONObject params, int timezoneOffset, Date curDate) {
        JSONObject numbers = new JSONObject();

        //发起
        List<Map<String, Object>> list = dao.selectList(Mapper.StatisticsMapper, "getPiStartedIn30Days", params);
        //JSONArray arr30days = new JSONArray();
        Map<String, Integer> countsStart = new HashMap<String, Integer>();
        JSONArray arr30start = new JSONArray();
        if (list!=null && list.size()>0) {
            for (int i=0; i<list.size(); i++) {
                countsStart.put((String) list.get(i).get("CREATE_DATE"), ((BigDecimal) list.get(i).get("COUNT")).intValue());
            }
        }
        for (int i=0; i<30; i++) {
            curDate.setTime(curDate.getTime() - 86400 * 1000);
            String dateStr = DateUtil.formatGMTDate(curDate, DateUtil.DATEFORMATDD_ZH);
            //arr30days.add((curDate.getMonth()+1)+"."+curDate.getDate());
            if (countsStart.containsKey(dateStr)) {
                arr30start.add(countsStart.get(dateStr));
            } else {
                arr30start.add(0);
            }
        }
        numbers.put("noa30_pistart", arr30start);
        //numbers.put("noa30_days", arr30days);

        //完成
        list = dao.selectList(Mapper.StatisticsMapper, "getPiFinishedIn30Days", params);
        Map<String, Integer> countsFinish = new HashMap<String, Integer>();
        JSONArray arr30Finish = new JSONArray();
        if (list!=null && list.size()>0) {
            for (int i=0; i<list.size(); i++) {
                countsFinish.put((String) list.get(i).get("COMPLETE_DATE"), ((BigDecimal) list.get(i).get("COUNT")).intValue());
            }
        }
        for (int i=0; i<30; i++) {
            curDate.setTime(curDate.getTime() - 86400 * 1000);
            String dateStr = DateUtil.formatGMTDate(curDate, DateUtil.DATEFORMATDD_ZH);
            if (countsFinish.containsKey(dateStr)) {
                arr30Finish.add(countsFinish.get(dateStr));
            } else {
                arr30Finish.add(0);
            }
        }
        numbers.put("noa30_pifinish", arr30Finish);

        //过期
        list = dao.selectList(Mapper.StatisticsMapper, "getPiOverdueIn30Days", params);
        Map<String, Integer> countsOverdue = new HashMap<String, Integer>();
        JSONArray arr30Overdue = new JSONArray();
        if (list!=null && list.size()>0) {
            for (int i=0; i<list.size(); i++) {
                countsOverdue.put((String) list.get(i).get("DUE_TIME"), ((BigDecimal) list.get(i).get("COUNT")).intValue());
            }
        }
        for (int i=0; i<30; i++) {
            curDate.setTime(curDate.getTime() - 86400 * 1000);
            String dateStr = DateUtil.formatGMTDate(curDate, DateUtil.DATEFORMATDD_ZH);
            if (countsOverdue.containsKey(dateStr)) {
                arr30Overdue.add(countsOverdue.get(dateStr));
            } else {
                arr30Overdue.add(0);
            }
        }
        numbers.put("noa30_pioverdue", arr30Overdue);

        return numbers;
    }

    /**
     * 30天内完成的任务完成数量，包括之前发起的
     * @param params
     * @param timezoneOffset
     * @param curDate
     * @return
     */
    private JSONObject getTrendOf30Ti(JSONObject params, int timezoneOffset, Date curDate) {
        JSONObject numbers = new JSONObject();
        List<Map<String, Object>> list = dao.selectList(Mapper.StatisticsMapper, "getTiHandledIn30Days", params);
        Map<String, Integer> counts = new HashMap<String, Integer>();
        JSONArray arr30 = new JSONArray();
        if (list!=null && list.size()>0) {
            for (int i=0; i<list.size(); i++) {
                counts.put((String) list.get(i).get("COMPLETE_DATE"), ((BigDecimal) list.get(i).get("COUNT")).intValue());
            }
        }
        for (int i=0; i<30; i++) {
            curDate.setTime(curDate.getTime() - 86400 * 1000);
            String dateStr = DateUtil.formatGMTDate(curDate, DateUtil.DATEFORMATDD_ZH);
            if (counts.containsKey(dateStr)) {
                arr30.add(counts.get(dateStr));
            } else {
                arr30.add(0);
            }
        }
        numbers.put("noa30_tihandle", arr30);
        return numbers;
    }

    /**
     * 获得发起最多的用户和发起数量
     * @param params
     * @return
     */
    private JSONObject getTopUsersOfPi(JSONObject params) {
        JSONObject numbers = new JSONObject();
        /*numbers.put("top5", JSONObject.parse("[\n" +
                "                {value: 32, start: 1, handle:31, name: '用户1'},\n" +
                "                {value: 19, start: 1, handle:18, name: '用户2'},\n" +
                "                {value: 60, start: 29, handle:31, name: '用户3'},\n" +
                "                {value: 21, start: 1, handle:20, name: '用户4'},\n" +
                "                {value: 120, start: 10, handle:100, name: '用户5'}\n" +
                "            ]"));*/
        List<Map<String, Object>> list = dao.selectList(Mapper.StatisticsMapper, "getTopUsersOfPi", params);
        JSONArray ja = new JSONArray();
        if (list!=null && list.size()>0) {
            for (Map<String, Object> countMap : list) {
                JSONObject js = new JSONObject();
                js.put("value", countMap.get("COUNT"));
                js.put("name", countMap.get("USER_NAME"));
                js.put("userId", countMap.get("USER_ID"));
                ja.add(js);
            }
        }
        numbers.put("toppi", ja);
        Map<String, Integer> countsStart = new HashMap<String, Integer>();
        return numbers;
    }

}
